#ifndef MEASURE_SIZE_TRACKER_MOVE_HANDLE_TO_VISITOR_H_
#define MEASURE_SIZE_TRACKER_MOVE_HANDLE_TO_VISITOR_H_

#if defined(_MSC_VER)
#pragma once
#endif

#include "geometry_elements.h"
#include "boost\variant\static_visitor.hpp"
#include <algorithm>
#include <cstdlib>

class move_handle_to_visitor : public boost::static_visitor<>
{
public:

  move_handle_to_visitor(int handle, const point_2 &point)
    : handle_(handle)
    , point_(point)
  {
  }

  ~move_handle_to_visitor(void)
  {
  }

public:

  void operator()(point_2& point) const
  {
    BOOST_ASSERT(false);
  }

  void operator()(segment_2& segment) const
  {
    switch (handle_)
    {
    case segment_2::kHitStart:
      segment.start = point_;
      break;

    case segment_2::kHitEnd:
      segment.end = point_;
      break;

    default:
      BOOST_ASSERT(false);
    }
  }

  void operator()(box_2& box) const
  {
    switch (handle_)
    {
    case box_2::kHitEast:
      box.bottom_right.x  = std::max(point_.x, box.top_left.x);
      break;

    case box_2::kHitNorthEast:
      box.bottom_right.x  = std::max(point_.x, box.top_left.x);
      box.top_left.y      = std::min(point_.y, box.bottom_right.y);
      break;

    case box_2::kHitNorth:
      box.top_left.y      = std::min(point_.y, box.bottom_right.y);
      break;

    case box_2::kHitNorthWest:
      box.top_left.x      = std::min(point_.x, box.bottom_right.x);
      box.top_left.y      = std::min(point_.y, box.bottom_right.y);
      break;

    case box_2::kHitWest:
      box.top_left.x      = std::min(point_.x, box.bottom_right.x);
      break;

    case box_2::kHitSouthWest:
      box.top_left.x      = std::min(point_.x, box.bottom_right.x);
      box.bottom_right.y  = std::max(point_.y, box.top_left.y);
      break;

    case box_2::kHitSouth:
      box.bottom_right.y  = std::max(point_.y, box.top_left.y);
      break;

    case box_2::kHitSouthEast:
      box.bottom_right.x  = std::max(point_.x, box.top_left.x);
      box.bottom_right.y  = std::max(point_.y, box.top_left.y);
      break;

    default:
      BOOST_ASSERT(false);
    }
  }

  void operator()(angle_box_2& angle_box) const
  {
    switch(handle_)
    {
    case angle_box_2::kHitEast:
      angle_box.move_east_handle_to(point_);
      break;

    case angle_box_2::kHitNorthEast:
      angle_box.move_north_east_handle_to(point_);
      break;

    case angle_box_2::kHitNorth:
      angle_box.move_north_handle_to(point_);
      break;

    case angle_box_2::kHitNorthWest:
      angle_box.move_north_west_handle_to(point_);
      break;

    case angle_box_2::kHitWest:
      angle_box.move_west_handle_to(point_);
      break;

    case angle_box_2::kHitSouthWest:
      angle_box.move_south_west_handle_to(point_);
      break;

    case angle_box_2::kHitSouth:
      angle_box.move_south_handle_to(point_);
      break;

    case angle_box_2::kHitSouthEast:
      angle_box.move_south_east_handle_to(point_);
      break;

    case angle_box_2::kHitTop:
      angle_box.move_top_handle_to(point_);
      break;

    case angle_box_2::kHitBottom:
      angle_box.move_bottom_handle_to(point_);
      break;

    case angle_box_2::kHitLeft:
      angle_box.move_left_handle_to(point_);
      break;

    case angle_box_2::kHitRight:
      angle_box.move_right_handle_to(point_);
      break;

    default:
      BOOST_ASSERT(false);
    }
  }

  void operator()(ring_2& ring) const
  {
    switch(handle_)
    {
    case ring_2::kHitBorder:
    case ring_2::kHitHandle:
      ring.radius = point_.distance(point_2(ring.center_x, ring.center_y));
      break;

    default:
      BOOST_ASSERT(false);
    }
  }

  void operator()(arc_2& arc) const
  {
    switch(handle_)
    {
    case arc_2::kHitBorder:
      arc.radius = point_.distance(point_2(arc.center_x, arc.center_y));
      break;

    case arc_2::kHitStart:
      {
        point_2   center(arc.center_x, arc.center_y);
        vector_2  vec       = point_ - center;
        int       vecAngle  = vec.angle();
        int       sweepOld  = angle_math::sweep(arc.start_angle, arc.end_angle);
        int       sweep     = angle_math::sweep(vecAngle, arc.end_angle);
        int       delta     = std::abs(sweep-sweepOld);

        if(delta<90) {
          arc.start_angle = vecAngle;
        }
      }
      break;

    case arc_2::kHitEnd:
      {
        point_2   center(arc.center_x, arc.center_y);
        vector_2  vec       = point_ - center;
        int       vecAngle  = vec.angle();
        int       sweepOld  = angle_math::sweep(arc.start_angle, arc.end_angle);
        int       sweep     = angle_math::sweep(arc.start_angle, vecAngle);
        int       delta     = std::abs(sweep-sweepOld);

        if(delta<90) {
          arc.end_angle = vecAngle;
        }
      }
      break;

    default:
      BOOST_ASSERT(false);
    }
  }

  void operator()(annulus_2& annulus) const
  {
    switch(handle_)
    {
    case annulus_2::kHitStartBorder:
      annulus.start_radius = point_.distance(point_2(annulus.center_x,
        annulus.center_y));
      break;

    case annulus_2::kHitEndBorder:
      annulus.end_radius = point_.distance(point_2(annulus.center_x,
        annulus.center_y));
      break;

    default:
      BOOST_ASSERT(false);
    }
  }

  void operator()(annulus_arc_2& as) const
  {
    switch(handle_)
    {
    case annulus_arc_2::kHitStartBorder:
      as.start_radius = point_.distance(point_2(as.center_x, as.center_y));
      break;

    case annulus_arc_2::kHitEndBorder:
      as.end_radius = point_.distance(point_2(as.center_x, as.center_y));
      break;


    case annulus_arc_2::kHitStartSpoke:
      {
        point_2   center(as.center_x, as.center_y);
        vector_2  vec       = point_ - center;
        int       vecAngle  = vec.angle();
        int       sweepOld  = angle_math::sweep(as.start_angle, as.end_angle);
        int       sweep     = angle_math::sweep(vecAngle, as.end_angle);
        int       delta     = std::abs(sweep-sweepOld);

        if(delta<90) {
          as.start_angle = vecAngle;
        }
      }
      break;

    case annulus_arc_2::kHitEndSpoke:
      {
        point_2   center(as.center_x, as.center_y);
        vector_2  vec       = point_ - center;
        int       vecAngle  = vec.angle();
        int       sweepOld  = angle_math::sweep(as.start_angle, as.end_angle);
        int       sweep     = angle_math::sweep(as.start_angle, vecAngle);
        int       delta     = std::abs(sweep-sweepOld);

        if(delta<90) {
          as.end_angle = vecAngle;
        }
      }
      break;

    case annulus_arc_2::kHitStartStart:
      {
        point_2   center(as.center_x, as.center_y);
        vector_2  vec       = point_ - center;
        int       vecAngle  = vec.angle();
        int       sweepOld  = angle_math::sweep(as.start_angle, as.end_angle);
        int       sweep     = angle_math::sweep(vecAngle, as.end_angle);
        int       delta     = std::abs(sweep-sweepOld);

        if(delta<90) {
          as.start_angle = vecAngle;
        }

        as.start_radius = point_.distance(point_2(as.center_x, as.center_y));
      }
      break;

    case annulus_arc_2::kHitEndStart:
      {
        point_2   center(as.center_x, as.center_y);
        vector_2  vec       = point_ - center;
        int       vecAngle  = vec.angle();
        int       sweepOld  = angle_math::sweep(as.start_angle, as.end_angle);
        int       sweep     = angle_math::sweep(vecAngle, as.end_angle);
        int       delta     = std::abs(sweep-sweepOld);

        if(delta<90) {
          as.start_angle = vecAngle;
        }

        as.end_radius = point_.distance(point_2(as.center_x, as.center_y));
      }
      break;

    case annulus_arc_2::kHitStartEnd:
      {
        point_2   center(as.center_x, as.center_y);
        vector_2  vec       = point_ - center;
        int       vecAngle  = vec.angle();
        int       sweepOld  = angle_math::sweep(as.start_angle, as.end_angle);
        int       sweep     = angle_math::sweep(as.start_angle, vecAngle);
        int       delta     = std::abs(sweep-sweepOld);

        if(delta<90) {
          as.end_angle = vecAngle;
        }

        as.start_radius = point_.distance(point_2(as.center_x, as.center_y));
      }
      break;

    case annulus_arc_2::kHitEndEnd:
      {
        point_2   center(as.center_x, as.center_y);
        vector_2  vec       = point_ - center;
        int       vecAngle  = vec.angle();
        int       sweepOld  = angle_math::sweep(as.start_angle, as.end_angle);
        int       sweep     = angle_math::sweep(as.start_angle, vecAngle);
        int       delta     = std::abs(sweep-sweepOld);

        if(delta<90) {
          as.end_angle = vecAngle;
        }

        as.end_radius = point_.distance(point_2(as.center_x, as.center_y));
      }
      break;

    default:
      BOOST_ASSERT(false);
    }
  }

  void operator()(polygon_2& polygon) const
  {
    BOOST_ASSERT(polygon_2::kHitOn<handle_&&handle_<polygon.num_points()*2);

    if (handle_&0x1) {
      point_2 sp = polygon.at(handle_/2);
      point_2 ep = polygon.at(((handle_+1)/2)%polygon.num_points());
    } else {
      polygon.at(handle_/2) = point_;
    }
  }

private:
  int handle_;
  const point_2 &point_;
};

#endif // MEASURE_SIZE_TRACKER_MOVE_HANDLE_TO_VISITOR_H_